package com.tywj.lib.net.retrofit

import androidx.collection.ArrayMap
import com.franmontiel.persistentcookiejar.PersistentCookieJar
import com.franmontiel.persistentcookiejar.cache.SetCookieCache
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor
import com.tywj.lib.core.common.util.AppUtil
import com.tywj.lib.core.common.util.LogUtil
import com.tywj.lib.net.retrofit.model.HttpHeader
import com.tywj.lib.net.retrofit.model.HttpMethod
import com.tywj.lib.net.retrofit.model.SSLParam
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.util.*
import java.util.concurrent.TimeUnit
import javax.net.ssl.HostnameVerifier


/**
 * <pre>
 *     author : June Yang
 *     time   : 2020/5/7
 *     desc   : 网络请求配置
 *     version: 1.0.0
 * </pre>
 */
class HttpConfig(
    baseUrl: String,
    interceptors: MutableList<Interceptor>,
    networkInterceptors: MutableList<Interceptor>,
    private val defaultConnectTimeout: Long,
    private val defaultReadTimeout: Long,
    private val defaultWriteTimeout: Long,
    retryOnConnectionFailure: Boolean,
    isUseCookie: Boolean,
    isUseCache: Boolean,
    logLevel: HttpLoggingInterceptor.Level,
    val commonHeaders: ArrayMap<String, String>,
    val commonParams: ArrayMap<String, String>,
    sslParam: SSLParam,
    hostnameVerifier: HostnameVerifier
) {

    companion object {
        const val CACHE_SIZE = 10 * 1024 * 1024L
        const val CACHE_DIR = "okhttp"
    }

    private val okHttpClient: OkHttpClient
    internal val retrofit: Retrofit
    internal val httpMethod: HttpMethod

    init {

        val okHttpClientBuilder = OkHttpClient.Builder()

        //设置超时时间
        okHttpClientBuilder.connectTimeout(defaultConnectTimeout, TimeUnit.MILLISECONDS)
        okHttpClientBuilder.readTimeout(defaultReadTimeout, TimeUnit.MILLISECONDS)
        okHttpClientBuilder.writeTimeout(defaultWriteTimeout, TimeUnit.MILLISECONDS)

        // 设置是连接失败时是否重试
        okHttpClientBuilder.retryOnConnectionFailure(retryOnConnectionFailure)

        // 添加拦截器
        interceptors.forEach { okHttpClientBuilder.addInterceptor(it) }
        networkInterceptors.forEach { okHttpClientBuilder.addNetworkInterceptor(it) }

        // 设置是否使用Cookie
        if (isUseCookie) {
            okHttpClientBuilder.cookieJar(
                PersistentCookieJar(
                    SetCookieCache(),
                    SharedPrefsCookiePersistor(AppUtil.context)
                )
            )
        }

        // 设置是否使用Cache
        if (isUseCache) {
            okHttpClientBuilder.cache(Cache(File(AppUtil.context.cacheDir, CACHE_DIR), CACHE_SIZE))
        }
        // 设置打印日志
        if (logLevel != HttpLoggingInterceptor.Level.NONE) {
            val httpLoggingInterceptor = HttpLoggingInterceptor {
                if (it.startsWith("{") || it.startsWith("[")) {
                    LogUtil.json(it, true)
                } else {
                    LogUtil.v(it, false)
                }
            }
            httpLoggingInterceptor.level = logLevel
            okHttpClientBuilder.addInterceptor(httpLoggingInterceptor)
        }

        // 配置https
        okHttpClientBuilder.sslSocketFactory(sslParam.sslSocketFactory, sslParam.trustManager)
        okHttpClientBuilder.hostnameVerifier(hostnameVerifier)

        okHttpClient = okHttpClientBuilder.build()

        retrofit = Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
            .callFactory { newCall(it) }
            .build()

        httpMethod = retrofit.create(HttpMethod::class.java)
    }

    private fun newCall(request: Request): Call {
        // 判断用户是否在请求中设置了超时时间，如果设置了移除该Header
        // 同时判断该超时时间是否和设置的通用超时时间是否相同，如果相同，不认为用户单为这个请求设置了单独的超时时间
        val builder = request.newBuilder()
        var connectTimeout = 0L
        request.header(HttpHeader.HEAD_SINGLE_REQUEST_CONNECT_TIMEOUT)?.let {
            val timeout = it.toLong()
            if (timeout != defaultConnectTimeout) {
                connectTimeout = timeout
            }
            builder.removeHeader(HttpHeader.HEAD_SINGLE_REQUEST_CONNECT_TIMEOUT)
        }

        var readTimeout = 0L
        request.header(HttpHeader.HEAD_SINGLE_REQUEST_READ_TIMEOUT)?.let {
            val timeout = it.toLong()
            if (timeout != defaultReadTimeout) {
                readTimeout = timeout
            }
            builder.removeHeader(HttpHeader.HEAD_SINGLE_REQUEST_READ_TIMEOUT)
        }

        var writeTimeout = 0L
        request.header(HttpHeader.HEAD_SINGLE_REQUEST_WRITE_TIMEOUT)?.let {
            val timeout = it.toLong()
            if (timeout != defaultWriteTimeout) {
                writeTimeout = timeout
            }
            builder.removeHeader(HttpHeader.HEAD_SINGLE_REQUEST_WRITE_TIMEOUT)
        }

        return if (connectTimeout + readTimeout + writeTimeout > 0L) {
            // 超时时间大于0，说明用户设置了新超时时间，基于原来的okHttpClient构建一个使用新的超时时间的okHttpClient执行网络请求
            okHttpClient.newBuilder()
                .connectTimeout(
                    if (connectTimeout == 0L) defaultConnectTimeout else connectTimeout,
                    TimeUnit.MILLISECONDS
                )
                .readTimeout(
                    if (readTimeout == 0L) defaultReadTimeout else readTimeout,
                    TimeUnit.MILLISECONDS
                )
                .writeTimeout(
                    if (writeTimeout == 0L) defaultWriteTimeout else writeTimeout,
                    TimeUnit.MILLISECONDS
                )
                .build()
                .newCall(builder.build())
        } else {
            // 用户没有设置超时时间或设置了通用超时时间一样的超时时间，使用默认的okHttpClient执行网络请求
            okHttpClient.newCall(request)
        }
    }

    /**
     * 网络请求配置构建者
     */
    class Builder {
        private var baseUrl = ""
        private var interceptors: ArrayList<Interceptor> = ArrayList()
        private var networkInterceptors: ArrayList<Interceptor> = ArrayList()
        private var defaultConnectTimeout = 10_000L
        private var defaultReadTimeout = 10_000L
        private var defaultWriteTimeout = 10_000L
        private var retryOnConnectionFailure = false
        private var isUseCookie = false
        private var isUseCache = false
        private var logLevel = HttpLoggingInterceptor.Level.NONE
        private val commonHeaders = ArrayMap<String, String>()
        private val commonParams = ArrayMap<String, String>()
        private var sslParam: SSLParam = HttpsUtil.getSslSocketFactory()
        private var hostnameVerifier: HostnameVerifier = HttpsUtil.UnSafeHostnameVerifier

        fun baseUrl(url: String): Builder {
            baseUrl = url
            return this
        }

        fun addInterceptor(interceptor: Interceptor): Builder {
            interceptors.add(interceptor)
            return this
        }

        fun addNetworkInterceptor(interceptor: Interceptor): Builder {
            networkInterceptors.add(interceptor)
            return this
        }

        /**
         * 连接超时时间
         * @param millis 单位是毫秒（默认10秒）
         */
        fun connectTimeoutMillis(millis: Long): Builder {
            if (millis <= 0) {
                throw IllegalArgumentException("connect timeout must Greater than 0")
            }
            defaultConnectTimeout = millis
            return this
        }

        /**
         * 读取超时时间
         * @param millis 单位是毫秒（默认10秒）
         */
        fun readTimeoutMillis(millis: Long): Builder {
            if (millis <= 0) {
                throw IllegalArgumentException("read timeout must Greater than 0")
            }
            defaultReadTimeout = millis
            return this
        }

        /**
         * 写入超时时间
         * @param millis 单位是毫秒（默认10秒）
         */
        fun writeTimeoutMillis(millis: Long): Builder {
            if (millis <= 0) {
                throw IllegalArgumentException("write timeout must Greater than 0")
            }
            defaultWriteTimeout = millis
            return this
        }

        /**
         * 连接失败时是否重新进行网络请求
         * @param retryOnConnectionFailure 默认为false
         */
        fun retryOnConnectionFailure(retryOnConnectionFailure: Boolean): Builder {
            this.retryOnConnectionFailure = retryOnConnectionFailure
            return this
        }

        /**
         * 是否开启cookie
         * @param isUseCookie 默认为false
         */
        fun useCookie(isUseCookie: Boolean): Builder {
            this.isUseCookie = isUseCookie
            return this
        }

        /**
         * 是否使用缓存
         * @param isUseCache 默认为false
         */
        fun useCache(isUseCache: Boolean): Builder {
            this.isUseCache = isUseCache
            return this
        }

        /**
         * 设置日志级别，参考[HttpLoggingInterceptor.Level]
         * @param level 默认为[HttpLoggingInterceptor.Level.NONE]
         */
        fun setLogLevel(level: HttpLoggingInterceptor.Level): Builder {
            logLevel = level
            return this
        }

        /**
         * 设置通用请求header
         * @param key header键
         * @param value header值
         */
        fun commonHeader(key: String, value: String): Builder {
            commonHeaders[key] = value
            return this
        }

        /**
         * 设置通用请求参数
         * @param key 参数键
         * @param value 参数值
         */
        fun commonParam(key: String, value: String): Builder {
            commonParams[key] = value
            return this
        }

        /**
         * 配置ssl
         * @param param ssl参数，默认不对证书做任何检查
         */
        fun sslSocketFactory(param: SSLParam): Builder {
            sslParam = param
            return this
        }

        /**
         * 主机名验证
         * @param verifier 默认允许所有主机名
         */
        fun hostnameVerifier(verifier: HostnameVerifier): Builder {
            hostnameVerifier = verifier
            return this
        }

        fun build(): HttpConfig {
            return HttpConfig(
                baseUrl, interceptors, networkInterceptors, defaultConnectTimeout
                , defaultReadTimeout, defaultWriteTimeout, retryOnConnectionFailure, isUseCookie
                , isUseCache, logLevel, commonHeaders, commonParams, sslParam, hostnameVerifier
            )
        }
    }
}